VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "Stack"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit


'This class utilizes a linked-list stack, since VB doesn't support a stack inherently.
'Since VB also doesn't support exceptions, and only numeric error codes, the stack
'will return nothing for pop and peek if no objects exist and a direct reference to the
'objects if they do exist.

Private vm_HeadItem_obj As LLNode
Private vm_numItems_int As Integer


Private Sub Class_Initialize()
Set vm_HeadItem_obj = Nothing
End Sub

Private Sub Class_Terminate()
Set vm_HeadItem_obj = Nothing
End Sub

Public Sub clear()
Set vm_HeadItem_obj = Nothing
vm_numItems_int = 0
End Sub

Friend Sub initWithHeadandCount(ByVal pr_Head As LLNode, pr_NumItems As Integer)
Set vm_HeadItem_obj = pr_Head
vm_numItems_int = pr_NumItems
End Sub

Public Sub push(ByVal pr_ObjectToAdd_obj As Variant)
Const CL_ERR_INVALIDPARAMETER As Integer = 91

Dim vl_newHead_udt As LLNode
Set vl_newHead_udt = New LLNode

If Not isVariantInitialized(pr_ObjectToAdd_obj) Then
    Err.Raise CL_ERR_INVALIDPARAMETER
End If

vl_newHead_udt.initWithDataAndNext pr_ObjectToAdd_obj, vm_HeadItem_obj
Set vm_HeadItem_obj = vl_newHead_udt
vm_numItems_int = vm_numItems_int + 1
End Sub

Public Function pop() As Variant
Dim vl_ReturnObject_obj As Variant

If vm_numItems_int = 0 Then
    Set vl_ReturnObject_obj = Nothing
Else
    vm_numItems_int = vm_numItems_int - 1
    assignVariantVariable vl_ReturnObject_obj, vm_HeadItem_obj
    
    Set vm_HeadItem_obj = vm_HeadItem_obj.nextItem
End If

assignVariantVariable pop, vl_ReturnObject_obj
End Function

Public Function peek() As Variant
Dim vl_ReturnObject_obj As Variant

If vm_numItems_int = 0 Then
    Set vl_ReturnObject_obj = Nothing
Else
    assignVariantVariable vl_ReturnObject_obj, vm_HeadItem_obj.Data
End If

assignVariantVariable peek, vl_ReturnObject_obj
End Function

Public Property Get numItems() As Integer
numItems = vm_numItems_int
End Property

Public Function isStackEmpty() As Boolean
isStackEmpty = (vm_HeadItem_obj Is Nothing)
End Function

'Friend method is used here in order to properly pass the head item after copy
'to iterator.
Friend Function getHeadItem() As LLNode
Set getHeadItem = vm_HeadItem_obj
End Function

'This clone method only clones one item deep. It does not perform a true deep copy,
'only copies the nodes themselves. For nodes that contain objects, the objects are not copied.
Public Function clone() As Stack
Dim vl_StackCopy As Stack
Set vl_StackCopy = New Stack

If Not vm_HeadItem_obj Is Nothing Then
    vl_StackCopy.initWithHeadandCount vm_HeadItem_obj.cloneAndAllNext, vm_numItems_int
End If

Set clone = vl_StackCopy

Set vl_StackCopy = Nothing
End Function

Private Function getNodeClone(ByVal pr_NodeToClone As LLNode, ByVal pr_CloneNext As Boolean) As LLNode
Dim vl_NewNode As LLNode
'TODO: Move to node

Set vl_NewNode = New LLNode

If pr_NodeToClone Is Nothing Then
    Set vl_NewNode = Nothing
Else
    Set vl_NewNode = New LLNode
    If pr_CloneNext Then
        vl_NewNode.initWithDataAndNext pr_NodeToClone.Data, getNodeClone(pr_NodeToClone.nextItem, True)
    Else
        vl_NewNode.initWithData pr_NodeToClone.Data
    End If
End If

Set getNodeClone = vl_NewNode
End Function

Private Function itemIsEmpty(ByVal pr_Item As Variant) As Boolean
If IsObject(pr_Item) Then
    itemIsEmpty = (pr_Item Is Nothing)
Else
    itemIsEmpty = (pr_Item = Empty)
End If
End Function

Public Function removeAndReturnByIndex(indexZeroBased As Integer) As Variant
Dim i As Integer
'Dim vl_TempStack As Stack
Dim vl_CurrentNode As LLNode
Dim vl_PreviousNode As LLNode
Dim vl_ReturnData As Variant

Const CL_INDEXOOB As Long = 35600

If (indexZeroBased > vm_numItems_int) Or _
        (indexZeroBased < 0) Then
    Err.Raise CL_INDEXOOB
End If

If indexZeroBased = 0 Then
    Set vm_HeadItem_obj = vm_HeadItem_obj.nextItem
Else
    
    Set vl_CurrentNode = vm_HeadItem_obj.nextItem
    Set vl_PreviousNode = vm_HeadItem_obj
    
    
    For i = 1 To indexZeroBased - 1
        Set vl_PreviousNode = vl_PreviousNode.nextItem
        Set vl_CurrentNode = vl_CurrentNode.nextItem
    Next i
    
    
    assignVariantVariable vl_ReturnData, vl_CurrentNode.Data
    
    
    Set vl_PreviousNode.nextItem = vl_CurrentNode.nextItem
    
End If


End Function

Public Function getIterator() As LLIterator
Dim vl_NewIterator As LLIterator
Set vl_NewIterator = New LLIterator

vl_NewIterator.init Me.clone.getHeadItem

Set getIterator = vl_NewIterator
End Function

Public Function getReversedStack() As Stack
Dim vl_NewStack As Stack
Dim vl_NewHead As LLNode
Dim vl_NewNext As LLNode

Dim vl_CurrentNode As LLNode
Dim vl_ItemCount As Integer

Set vl_CurrentNode = vm_HeadItem_obj

Do Until vl_CurrentNode Is Nothing
    Set vl_NewNext = vl_NewHead
    Set vl_NewHead = New LLNode
    vl_NewHead.initWithDataAndNext vl_CurrentNode.Data, vl_NewNext
    
    Set vl_CurrentNode = vl_CurrentNode.nextItem
    vl_ItemCount = vl_ItemCount + 1
Loop

Set vl_NewStack = New Stack
vl_NewStack.initWithHeadandCount vl_NewHead, vl_ItemCount

Set getReversedStack = vl_NewStack
End Function

Private Function isVariantInitialized(ByVal pr_Variant As Variant) As Boolean
Dim vl_Return As Boolean


If Not IsEmpty(pr_Variant) Then
    If IsObject(pr_Variant) Then
        If Not pr_Variant Is Nothing Then
            vl_Return = True
        End If
    Else
        vl_Return = True
    End If
End If

isVariantInitialized = vl_Return
End Function
